package job.work.service.system.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.google.common.collect.Lists;
import job.work.dao.system.dao.DecMenuDao;
import job.work.dao.system.dao.DecRoleMenuDao;
import job.work.domain.response.CommonCode;
import job.work.domain.response.PageResult;
import job.work.domain.response.ResponseResult;
import job.work.domain.system.entity.pojo.DecMenu;
import job.work.domain.system.entity.pojo.DecRoleMenu;
import job.work.domain.system.request.MenuPageRequest;
import job.work.service.system.service.DecMenuService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

@Service
public class DecMenuServiceImpl implements DecMenuService {


    /**
     * 菜单接口
     */
    @Autowired
    DecMenuDao decMenuDao;

    Logger logger = LoggerFactory.getLogger(DecMenuServiceImpl.class);

    /**
     * 菜单角色中间表接口
     */
    @Autowired
    DecRoleMenuDao decRoleMenuDao;

    /**
     * 分页查询所有菜单
     *
     * @param resultRequest 分页参数
     * @return 分页后的数据
     */
    @Override
    public ResponseResult<PageResult<DecMenu>> findPageMenu(MenuPageRequest resultRequest) {

        //分页参数并排序
        Pageable pageable = PageRequest.of(resultRequest.getPageNumber() - 1, resultRequest.getPageSize());
        Specification<DecMenu> specification = new Specification<DecMenu>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = Lists.newArrayList();
                if (!StringUtils.isEmpty(resultRequest.getName())) {//菜单名称
                    Predicate menuName = criteriaBuilder.like(root.get("menuName"), "%" + resultRequest.getName() + "%");
                    Predicate url = criteriaBuilder.like(root.get("url"), "%" + resultRequest.getName() + "%");
                    Predicate or = criteriaBuilder.or(menuName, url);
                    predicates.add(or);
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            }
        };

        Page<DecMenu> all = decMenuDao.findAll(specification, pageable);
        return new ResponseResult<>(CommonCode.SUCCESS, new PageResult<>(all.getTotalElements(), all.getContent()));
    }


    /**
     * 查询所有菜单
     * @return 返回树状菜单
     */
    @Override
    public ResponseResult findAllMenu() {
        long startTime = System.currentTimeMillis();

        //List<DecMenu> decMenus = decMenuDao.findAll(Sort.by(Sort.Order.asc("sort")));
        List<DecMenu> decMenus = decMenuDao.findNotByShowId(1);

        if (decMenus.size() != 0) {
            List<DecMenu> menuTree = getMenuTree(decMenus);
            long endTime = System.currentTimeMillis();
            logger.info("查询所有菜单并且封装好的时间！[{}ms]", endTime - startTime);
            return new ResponseResult<>(CommonCode.SUCCESS, menuTree);
        }
        long endTime = System.currentTimeMillis();
        logger.info("查询菜单为空！[{}ms]", endTime - startTime);
        return ResponseResult.SUCCESS();
    }

    /**
     * 根据每一角色生成菜单结构
     *
     * @param roleId 角色ID
     * @return 菜单集合
     */
    @Override
    public List<DecMenu> findRoleMenu(Long roleId) {
        long startTime = System.currentTimeMillis();
        List<DecMenu> decMenus = this.rolesList(roleId);

        List<DecMenu> menuTree = getMenuTree(decMenus);
        long endTime = System.currentTimeMillis();
        logger.info("根据每一角色生成菜单结构！[{}ms]", endTime - startTime);
        return menuTree;
    }


    public List<DecMenu> rolesList (Long roleId){
        //根据角色ID 查询角色 菜单
        List<DecRoleMenu> byRoleId = decRoleMenuDao.findByRoleId(roleId);
        //装菜单Id集合
        Set<Long> decMenuId = new HashSet<>();
        byRoleId.forEach(decRoleMenu -> {
            decMenuId.add(decRoleMenu.getMenuId());
        });
        //装 查询出来的菜单集合
        return decMenuDao.findByIdIn(decMenuId);
    }


    /**
     * 获取组装好的菜单
     * 以树的形式显示
     * @param menusRoot menusRoot
     * @return List
     */
    @Override
    public List<DecMenu> getMenuTree(List<DecMenu> menusRoot) {
        //生成一级菜单
        List<DecMenu> menusResult = new ArrayList<>();
        Iterator<DecMenu> iterator = menusRoot.iterator();
        while (iterator.hasNext()) {
            DecMenu menu = iterator.next();
            Long pId = menu.getPid();
            if (pId != null) {
                if (pId == 0) {
                    // 如果是一级菜单就添加进去
                    menusResult.add(menu);
                    iterator.remove();
                }
            }
        }

        //添加一级菜单下面的子菜单
        for (DecMenu menu : menusResult) {
            //一级菜单下的子菜单
            menu.setChildren(getChildTree(menu.getId(), menusRoot));
        }
        return menusResult;
    }

    /**
     * 获取一级菜单的子菜单
     * @param id        菜单Id
     * @param menusRoot menusRoot
     * @return List
     */
    private List<DecMenu> getChildTree(Long id, List<DecMenu> menusRoot) {
        //下级菜单
        List<DecMenu> menusChild = new ArrayList<>();
        for (DecMenu menu : menusRoot) {
            if (menu.getPid() != null) {
                //如果不是父菜单
                if (0 != menu.getPid()) {
                    //传入ID 等于父ID
                    if (menu.getPid().equals(id)) {
                        //添加子菜单
                        menusChild.add(menu);
                    }
                }
            }
        }
        //遍历二级菜单
        for (DecMenu menu : menusChild) {
            if (menu.getPid() != null) {
                //如果父ID不等于0
                if (0 != menu.getPid()) {
                    menu.setChildren(getChildTree(menu.getId(), menusRoot));
                }
            }
        }
        if (menusChild.size() == 0) {
            return null;
        }
        return menusChild;
    }

    /**
     * 保存/修改 菜单节点
     *
     * @param decMenu 菜单
     * @return ResponseResult
     */
    @Override
    @Transactional
    public ResponseResult saveMenu(DecMenu decMenu) {
        if (decMenu != null) {

            DecMenu byUrl = decMenuDao.findByUrl(decMenu.getUrl());
            if (byUrl != null) {
                if (decMenu.getId() == null || !decMenu.getId().equals(byUrl.getId())) {
                    return new ResponseResult(CommonCode.FAIL, "菜单路径重复!");
                }
            }
            DecMenu menuName = decMenuDao.findByMenuName(decMenu.getMenuName());
            if (menuName != null) {
                if (decMenu.getId() == null || !decMenu.getId().equals(menuName.getId())) {
                    return new ResponseResult(CommonCode.FAIL, "菜单名称重复!");
                }
            }
            decMenuDao.save(decMenu);
            return ResponseResult.SUCCESS();
        }
        return ResponseResult.FAIL();
    }

    /**
     * 删除菜单
     *
     * @param id 根据ID删除菜单
     * @return ResponseResult
     */
    @Transactional
    @Override
    public ResponseResult delectMenu(Long id) {
        if (id != null) {
            //按照数结构查找所有菜单
            List<DecMenu> menus = decMenuDao.findAll();
            if (menus.size() != 0) {
                Optional<DecMenu> byId = decMenuDao.findById(id);
                decRoleMenuDao.deleteAllByMenuId(id);
                byId.ifPresent(decMenu -> decMenuDao.deleteByIdAndPid(decMenu.getId(), decMenu.getPid()));
                return ResponseResult.SUCCESS();
            }
            return ResponseResult.SUCCESS();
        }
        return new ResponseResult<>(CommonCode.PARAM_ERROR);
    }

    /**
     * 获取指定菜单的根节点
     *
     * @param id        根据ID查询当前菜单的节点
     * @param menusRoot 菜单集合
     * @return 封装好的菜单集合
     */
    @Override
    public List<DecMenu> getRootTree(Long id, List<DecMenu> menusRoot) {
        //生成一级菜单
        List<DecMenu> menusResult = new ArrayList<>();
        DecMenu backgroundMenu = new DecMenu();
        backgroundMenu.setId(id);
        //获取二级以及以下子菜单
        backgroundMenu.setChildren(findDeleteChilden(id, menusRoot));
        menusResult.add(backgroundMenu);
        return menusResult;
    }

    /**
     * 获取指定菜单的根节点下的子菜单
     *
     * @param id        菜单ID
     * @param menusRoot 菜单集合
     * @return 封装好的菜单集合
     */
    @Override
    public List<DecMenu> findDeleteChilden(Long id, List<DecMenu> menusRoot) {
        List<DecMenu> menusChild = new ArrayList<>();
        for (DecMenu menu : menusRoot) {
            if (menu.getPid().equals(id)) {
                menusChild.add(menu);
            }
        }
        for (DecMenu menu : menusChild) {
            menu.setChildren(findDeleteChilden(menu.getId(), menusRoot));
        }
        //不满足条件就跳出循环
        if (menusChild.size() == 0) {
            return null;
        }
        return menusChild;
    }

}
